using Gj;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;

// using SimpleBallistic;

public partial class CastTrailSystem : SystemBase
{
    private EntityQuery auto_Group;
    private EntityQuery shoot_Group;
    private EntityQuery target_Group;
    private EntityQuery throw_Group;

    protected override void OnCreate()
    {
        shoot_Group = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadWrite<CastShootTrailData>(),
                ComponentType.ReadWrite<LocalTransform>(),
                ComponentType.ReadWrite<NextTranslation>()
            }
        });

        target_Group = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadWrite<CastTargetTrailData>(),
                ComponentType.ReadWrite<LocalTransform>(),
                ComponentType.ReadWrite<NextTranslation>()
            }
        });

        throw_Group = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadWrite<CastThrowTrailData>(),
                ComponentType.ReadWrite<LocalTransform>(),
                ComponentType.ReadWrite<NextTranslation>()
            }
        });

        auto_Group = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadWrite<CastAutoTrailData>(),
                ComponentType.ReadWrite<LocalTransform>(),
                ComponentType.ReadWrite<NextTranslation>()
            }
        });
    }

    // OnUpdate runs on the main thread.
    protected override void OnUpdate()
    {
        var inputDeps = Dependency;
        var time = SystemAPI.Time.DeltaTime;
        inputDeps = new ShootJob
        {
            DeltaTime = time
        }.Schedule(shoot_Group, inputDeps);

        inputDeps = new TargetJob
        {
            DeltaTime = time
        }.Schedule(target_Group, inputDeps);

        inputDeps = new ThrowJob
        {
            DeltaTime = time
        }.Schedule(throw_Group, inputDeps);

        inputDeps = new AutoJob
        {
            DeltaTime = time
        }.Schedule(auto_Group, inputDeps);

        inputDeps.Complete();
    }

    [BurstCompile]
    private partial struct ShootJob : IJobEntity
    {
        [ReadOnly] public float DeltaTime;

        public void Execute(ref CastShootTrailData trailData, ref LocalTransform translation,
            ref NextTranslation nextTranslation)
        {
            trailData.time += DeltaTime;
            translation.Position = trailData.direction * trailData.speed * trailData.time + trailData.origin;
            nextTranslation.Value = trailData.direction * trailData.speed * (trailData.time + trailData.nextTime) +
                                    trailData.origin;
            translation.Rotation = (nextTranslation.Value - translation.Position).Equals(float3.zero)
                ? Quaternion.identity
                : Quaternion.LookRotation(nextTranslation.Value - translation.Position, math.up());
        }
    }

    [BurstCompile]
    private partial struct TargetJob : IJobEntity
    {
        [ReadOnly] public float DeltaTime;

        public void Execute(ref CastTargetTrailData trailData, ref LocalTransform translation,
            ref NextTranslation nextTranslation)
        {
            trailData.time += DeltaTime;
            translation.Position =
                Vector3.Lerp(trailData.origin, trailData.target, trailData.time / trailData.totalTime);
            nextTranslation.Value = Vector3.Lerp(trailData.origin, trailData.target,
                (trailData.time + trailData.nextTime) / trailData.totalTime);
            translation.Rotation = (nextTranslation.Value - translation.Position).Equals(float3.zero)
                ? Quaternion.identity
                : Quaternion.LookRotation(nextTranslation.Value - translation.Position, math.up());
        }
    }

    [BurstCompile]
    private partial struct ThrowJob : IJobEntity
    {
        [ReadOnly] public float DeltaTime;

        public void Execute(ref CastThrowTrailData trailData, ref LocalTransform translation,
            ref NextTranslation nextTranslation)
        {
            trailData.time += DeltaTime;
            translation.Position = GetPosition(trailData.origin, trailData.direction, trailData.speed, trailData.angle,
                trailData.time, trailData.gravity);
            nextTranslation.Value = GetPosition(trailData.origin, trailData.direction, trailData.speed, trailData.angle,
                trailData.time + trailData.nextTime,
                trailData.gravity);
            translation.Rotation = (nextTranslation.Value - translation.Position).Equals(float3.zero)
                ? Quaternion.identity
                : Quaternion.LookRotation(nextTranslation.Value - translation.Position, math.up());
        }

        public float3 GetPosition(float3 origin, float3 direction, float speed, float angle, float time, float gravity)
        {
            // return Ballistics.GetPositionAtTime(origin, direction, speed, angle, time);
            var num1 = speed * (math.cos(angle * (math.PI / 180f)) * time);
            var num2 = (float)(speed * (double)math.sin(angle * (math.PI / 180f)) * time +
                               0.5 * gravity * math.pow(time, 2f));
            var vector3_1 = direction * num1;
            var vector3_2 = math.up() * num2;
            var vector3_3 = origin;
            return vector3_1 + vector3_3 + vector3_2;
        }
    }

    [BurstCompile]
    private partial struct TrackJob : IJobEntity
    {
        [ReadOnly] public float DeltaTime;

        public void Execute(in CastTrackTrailData trailData, ref LocalTransform translation,
            in NextTranslation nextTranslation)
        {
            translation.Position = (Quaternion)translation.Rotation * Vector3.forward * DeltaTime * trailData.speed;
            var direction = Vector3.Normalize(trailData.targetPoint - translation.Position);
            var targetRotation = Quaternion.LookRotation(direction);
            translation.Rotation =
                Quaternion.RotateTowards(translation.Rotation, targetRotation, trailData.rotateSpeed);
        }
    }

    [BurstCompile]
    private partial struct AutoJob : IJobEntity
    {
        [ReadOnly] public float DeltaTime;

        public void Execute(ref CastAutoTrailData trailData, ref LocalTransform translation,
            ref NextTranslation nextTranslation)
        {
            trailData.time += DeltaTime;
            translation.Position = GetPosition(trailData.time / trailData.totalTime, trailData.origin, trailData.launch,
                trailData.target);
            nextTranslation.Value = GetPosition((trailData.time + trailData.nextTime) / trailData.totalTime,
                trailData.origin, trailData.launch, trailData.target);
            translation.Rotation = Quaternion.LookRotation(nextTranslation.Value - translation.Position, math.up());
        }

        public float3 GetPosition(float value, float3 origin, float3 launch, float3 target)
        {
            var a = MathTools.Lerp(origin, launch, value);
            var b = MathTools.Lerp(launch, target, value);
            return MathTools.Lerp(a, b, value);
        }
    }
}